fix(spec-parity): render the six Tier-1 spec values right instead of silently wrong (#2941) - #2993
Merged
Merged
Conversation
…silently wrong (#2941) Every row here validated at authoring time and produced output that looked correct and wasn't (#2901 Tier 1 — worse than a blank cell, because nobody asks why a plausible number is wrong): - PivotTable: an out-of-vocabulary aggregation (`count_distinct`, `array_agg`, `string_agg` — engine names with no client renderer) returned a SUM through the `default:` branch. It now refuses loudly with a visible notice; the implemented set is pinned to the spec's 5-name `ChartAggregateFunctionSchema`. - Report chart: 10 of 19 `ChartTypeSchema` values silently drew a bar. `planReportChart` now classifies all 19 — the 12 series families reach the generic chart verbatim (`horizontal-bar` stays horizontal), the 5 single-value families render the measure as a server-aggregated number (dimensionless dataset query — no client math), `table`/`pivot` add no duplicate chart, and out-of-spec values get a visible notice. - `selection.type: 'single'`: `selectable` was a bare truthy, so single rendered the full multi-select UX. The data-table now enforces replace-on-select with no select-all header, and ObjectGrid stops offering the cross-page "select all N matching" escalation in single mode. - Filter `type: 'select'`: the spec names both `select` and `multi-select`, so `select` is single-choice. It now renders radios and replaces the pick (badge × clears); restored/default multi-value selections clamp to one. Inferred (omitted) types keep the historical multi-check UX. - `addRecord.position: 'both'` collapsed to `top` through a binary ternary; both buttons now render. - `tabular` vs `summary` was resolved from whether `rows` was non-empty; the DECLARED type now picks the branch: summary (and degraded matrix) carries a server-computed grand-total footer (`totals: { groupings: [[]] }`, ADR-0021 red line respected), tabular is the same selection as a plain list. Each fix lands with a spec-parity guard per the #2897 template (`summary-spec-parity.test.ts`) plus behavior tests; `components`, `plugin-dashboard` and `plugin-report` gain the `@objectstack/spec` devDependency that makes those guards possible (`components` also pins `zod@^4` so the spec resolves to the same store instance as the rest of the repo instead of forking a second `zod@3` peer variant via shadcn's MCP SDK). Refs #2941, #2901 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
This was referenced Jul 30, 2026
Closed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #2941 (Tier 1 of the #2901 audit — spec values that validate and then render silently wrong output; audit doc).
Every finding below produced output that looked correct and wasn't. Each fix lands with a spec-parity guard per the #2897 template (
summary-spec-parity.test.ts) plus behavior tests, so the pair fails the moment either side moves again.The six fixes
PivotTable.tsx)count_distinct/array_agg/string_agg(engine-level names with no client renderer) returned a sum — a plausible wrong total, no signalrole="alert"notice; the implemented set is pinned to the spec's 5-nameChartAggregateFunctionSchema(the UI-side subset the spec carved out for exactly this reason). Thedefault:branch now returnsNaNas a tripwire, unreachable behind the gateDatasetReportRenderer.tsx)ChartTypeSchemavalues silently drew a bar chartplanReportChartclassifies all 19: the 12 series families reach the generic chart verbatim (horizontal-barstays horizontal,funnel/treemap/sankeydraw their real shapes — the renderer grew them since the audit); the 5 single-value families (gauge/solid-gauge/metric/kpi/bullet) render the measure as a number from a dimensionless dataset query (server-aggregated; the spec's own comment calls them "honest single-value variants pending a real dial renderer");table/pivotadd no duplicate chart (the grouped table beneath is that rendering); out-of-spec values get a visible notice instead of a guessed barselection.type: 'single'rendered multi-select (data-table.tsx←ObjectGrid.tsx)selectablewas consumed as a bare truthy: per-row checkboxes and select-all,singlenever distinguishedsingleis a real mode: replace-on-select (never two rows), no select-all header, and ObjectGrid stops offering the cross-page "select all N matching" escalation. Legacytruekeeps meaning multi-selecttype: 'select'rendered multi-check (UserFilters.tsx)selectandmulti-select, which settles the semantic question:selectis single-choice. It renders radios, picking replaces (badge × clears), and restored/default multi-value selections clamp to one. Authored types only — an omitted type is inferred from the field and keeps the historical multi-check UXaddRecord.position: 'both'collapsed totop(ListView.tsx)resolveAddRecordPlacementmaps all three spec values;bothrenders both buttons. (Discovered adjacent, left alone: the renderer's implicit default istopwhile the spec's declared default isbottom— moving stored views' button is a UX change beyond this PR; noted in the resolver's doc comment)tabularvssummaryresolved from data, not type (DatasetReportRenderer.tsx)report.rowshappened to be non-emptyresolveReportPresentationmakes the DECLARED type pick the branch.summary("grouped by row") now carries a server-computed grand-total footer —totals: { groupings: [[]] }, same machinery as the matrix path, never recombined client-side (ADR-0021 red line), older servers degrade to no footer.tabular("simple list") is the same selection as a plain list with no totals. Joined blocks get the same per-block dispatchSemantic decisions the issue asked for
selection.type: 'single'— replace-on-select radio semantics and a hard cap at one (they coincide: selecting row B deselects row A; unchecking clears). The spec'ssingle | multiplecontrast pair plus every mainstream grid's behavior settle this.filter type: 'select'vs'multi-select'— the enum contains both names, soselectmust mean the thingmulti-selectisn't: one value at a time.Guard prerequisite
components,plugin-dashboard,plugin-reportgain the@objectstack/specdevDependency (the issue's noted prerequisite;plugin-list/plugin-gridalready had it).componentsalso pinszod@^4.4.3— without it, shadcn's MCP SDK leakszod@3into the importer context and pnpm forks a second store instance of the spec (spec@17.0.0-rc.0(ai@7.0.37(zod@3.25.76))), the exact two-instances failure mode the audit's method note warns about. The lockfile now resolves one variant repo-wide.Verification
import('../renderers')30 s hook timeout under a contended machine; all 73 tests pass re-run in isolation).tsc --noEmitclean on all five packages; eslint 0 errors on every touched file.Refs #2901. Tier 2 (#2942), Tier 3 (#2943), forks (#2944) and vocabulary consolidation (#2945) stay separate.
🤖 Generated with Claude Code